﻿using Microsoft.EntityFrameworkCore.Metadata.Internal;
using static System.Runtime.InteropServices.Marshalling.IIUnknownCacheStrategy;

namespace _5_NovaAdmin.Infrastructure.Extensions;

/// <summary>
/// 处理实体与表结构迁移
/// </summary>
public class MigrationHandler
{
    private readonly ISqlSugarClient _db;
    private readonly IWebHostEnvironment _env;
    private readonly ILogger<MigrationHandler> _logger;
    public MigrationHandler(ISqlSugarClient db, IWebHostEnvironment env, ILogger<MigrationHandler> logger)
    {
        _db = db;
        _env = env;
        _logger = logger;
    }

    /// <summary>
    /// 字段与数据类型的映射字典
    /// </summary>
    Dictionary<string, string> typeMapping = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
    {
        { "int", "int" },
        { "bigint", "long" },
        { "smallint", "short" },
        { "tinyint", "byte" },
        { "float", "double" },   // SQL Server的float对应C#的double
        { "real", "float" },     // SQL Server的real对应C#的float
        { "double", "double" },  // MySQL的double
        { "decimal", "decimal" },
        { "numeric", "decimal" },
        { "varchar", "string" },
        { "nvarchar", "string" },
        { "text", "string" },
        { "char", "string" },    // 普通char默认映射为string
        { "date", "DateTime" },
        { "datetime", "DateTime" },
        { "datetime2", "DateTime" },
        { "smalldatetime", "DateTime" },
        { "timestamp", "DateTime" },
        { "uniqueidentifier", "Guid" },
        { "bit", "bool" },
        { "binary", "byte[]" },
        { "varbinary", "byte[]" },
        { "image", "byte[]" },
        { "time", "TimeSpan" }
    };

    /// <summary>
    /// 数据库 → 实体（逆向工程）
    /// </summary>
    public void GenerateEntitiesFromDb()
    {
        // 获取当前项目的根目录（无论开发还是运行环境）
        string projectRoot = Directory.GetCurrentDirectory().Replace(@"\bin\Debug\net8.0", string.Empty);
        Console.WriteLine($"projectRoot:{projectRoot}");

        // 获取解决方案目录（上级目录）
        string solutionDir = Directory.GetParent(projectRoot)?.FullName
            ?? throw new DirectoryNotFoundException("无法找到解决方案目录");

        // 组合类库的源代码路径
        string librarySourcePath = Path.Combine(
            solutionDir,
            "3`NovaAdmin.Domain"     // 类库项目文件夹名称
        );

        _db.DbFirst.IsCreateAttribute().SettingClassTemplate(template =>
        {
            // 自定义 ClassTemplate 模板
            return @"
namespace {Namespace};

[Table(""{ClassName}"")]
public class {ClassName} : Entity<Guid>, IAggregateRoot
{
    public {ClassName}() { } 
{PropertyName}
}
";
        }).SettingPropertyTemplate((columns, temp, type) =>
        {
            // 排除 Id ...字段 不生成
            if (new[] { "Id", "CreateOpId", "CreateOpName", "CreateOpDate", "EditOpId", "EditOpName", "EditOpDate" }.Contains(columns.DbColumnName))
            {
                return string.Empty;
            }

            // 生成其他属性
            return $@"
    public {type} {columns.DbColumnName} {{ get; set; }}";
        }).SettingPropertyDescriptionTemplate(_ => string.Empty)
          .CreateClassFile(Path.Combine(librarySourcePath, "Entities"), "_3_NovaAdmin.Domain.Entities");
    }

    /// <summary>
    /// 数据库 → 实体（逆向工程）
    /// </summary>
    /// <param name="outputFolder"></param>
    /// <param name="tableNames"></param>
    public void GenerateDomainEntitiesFromDb(string outputFolder, IEnumerable<string>? tableNames = null)
    {
        var tablesToProcess = GetTablesToProcess(tableNames);

        foreach (var table in tablesToProcess)
        {
            // true 走缓存 false不走缓存  
            var columns = _db.DbMaintenance.GetColumnInfosByTableName(table.Name, false);
            GenerateEntityClass(table, columns, outputFolder);
        }
    }

    /// <summary>
    /// 获取表信息
    /// </summary>
    /// <param name="tableNames"></param>
    /// <returns></returns>
    private List<DbTableInfo> GetTablesToProcess(IEnumerable<string>? tableNames)
    {
        // true 走缓存 false不走缓存  
        var tables = _db.DbMaintenance.GetTableInfoList(false);

        // 传了表名用表
        if (tableNames?.Any() == true)
        {
            // 使用HashSet提高查找性能
            var nameSet = new HashSet<string>(tableNames, StringComparer.OrdinalIgnoreCase);
            return tables.Where(t => nameSet.Contains(t.Name)).ToList();
        }

        return tables;
    }

    /// <summary>
    /// 生成实体类
    /// </summary>
    /// <param name="table"></param>
    /// <param name="columns"></param>
    /// <param name="outputFolder"></param>
    /// <exception cref="DirectoryNotFoundException"></exception>
    private void GenerateEntityClass(DbTableInfo table, List<DbColumnInfo> columns, string outputFolder)
    {
        var classContent = BuildEntityClassContent(table, columns);
        SaveEntityClassToFile(table.Name, classContent, outputFolder);
    }

    private string BuildEntityClassContent(DbTableInfo table, List<DbColumnInfo> columns)
    {
        var ignoreColumns = new[] { "Id", "CreateOpId", "CreateOpName", "CreateOpDate", "EditOpId", "EditOpName", "EditOpDate" };
        var guidFields = GetGuidFields(columns, ignoreColumns);
        bool hasIdField = columns.Any(c => c.DbColumnName == "Id");

        var classBuilder = new StringBuilder();

        BuildClassHeader(classBuilder, table, hasIdField);
        BuildConstructors(classBuilder, table.Name, hasIdField, guidFields);
        BuildProperties(classBuilder, columns, ignoreColumns, hasIdField, guidFields);
        BuildGetKeysMethod(classBuilder, hasIdField, guidFields);

        classBuilder.AppendLine("}");

        return classBuilder.ToString();
    }

    private List<DbColumnInfo> GetGuidFields(List<DbColumnInfo> columns, string[] ignoreColumns)
    {
        return columns
            .Where(c => GetCSharpType(c.DataType, c.Length) == "Guid" &&
                       !ignoreColumns.Contains(c.DbColumnName))
            .ToList();
    }

    private void BuildClassHeader(StringBuilder builder, DbTableInfo table, bool hasIdField)
    {
        builder.AppendLine("namespace _3_NovaAdmin.Domain.Entities;");
        builder.AppendLine();
        builder.AppendLine($"[Table(\"{table.Name}\")]");
        builder.AppendLine(hasIdField
            ? $"public class {table.Name} : Entity<Guid>, IAggregateRoot"
            : $"public class {table.Name} : CompositeEntity<Guid>");
        builder.AppendLine("{");
    }

    private void BuildConstructors(StringBuilder builder, string className, bool hasIdField, List<DbColumnInfo> guidFields)
    {
        builder.AppendLine($"    public {className}() {{ }}");

        if (!hasIdField && guidFields.Any())
        {
            var parameters = string.Join(", ", guidFields.Select(f => $"Guid {f.DbColumnName}"));
            builder.AppendLine($"    public {className}({parameters})");
            builder.AppendLine("    {");
            foreach (var field in guidFields)
            {
                builder.AppendLine($"        this.{field.DbColumnName} = {field.DbColumnName};");
            }
            builder.AppendLine("    }");
            builder.AppendLine();
        }
    }

    private void BuildProperties(StringBuilder builder, List<DbColumnInfo> columns, string[] ignoreColumns, bool hasIdField, List<DbColumnInfo> guidFields)
    {
        foreach (var column in columns)
        {
            if (ignoreColumns.Contains(column.DbColumnName))
                continue;

            string csharpType = GetCSharpType(column.DataType, column.Length);

            if (!hasIdField && guidFields.Any(f => f.DbColumnName == column.DbColumnName))
            {
                builder.AppendLine($"    [CompositeKey]");
            }

            builder.AppendLine($"    public {csharpType} {column.DbColumnName} {{ get; set; }}");
        }
    }

    private void BuildGetKeysMethod(StringBuilder builder, bool hasIdField, List<DbColumnInfo> guidFields)
    {
        if (!hasIdField && guidFields.Any())
        {
            builder.AppendLine();
            var keys = string.Join(", ", guidFields.Select(f => f.DbColumnName));
            builder.AppendLine($"    public override object[] GetKeys() => new object[] {{ {keys} }};");
        }
    }

    private void SaveEntityClassToFile(string className, string content, string outputFolder)
    {
        string projectRoot = Directory.GetCurrentDirectory().Replace(@"\bin\Debug\net8.0", string.Empty);
        string solutionDir = Directory.GetParent(projectRoot)?.FullName
            ?? throw new DirectoryNotFoundException("无法找到解决方案目录");

        string librarySourcePath = Path.Combine(
            solutionDir,
            "3`NovaAdmin.Domain",
            $"{outputFolder}"
        );

        Directory.CreateDirectory(librarySourcePath);
        File.WriteAllText(Path.Combine(librarySourcePath, $"{className}.cs"), content);
    }


    /// <summary>
    /// 根据数据库类型得到c#的类型
    /// </summary>
    /// <param name="dbType"></param>
    /// <param name="length"></param>
    /// <returns></returns>
    private string GetCSharpType(string dbType, int? length = null)
    {
        // 先处理特殊长度类型
        if (dbType.Equals("char", StringComparison.OrdinalIgnoreCase) && length == 36)
        {
            return "Guid";
        }

        // 在字典中查找对应的 C# 数据类型  
        if (typeMapping.TryGetValue(dbType.ToLower(), out string csharpType))
        {
            return csharpType;
        }
        return "object"; // 默认类型  
    }


    /// <summary>
    /// 实体 → 数据库（正向同步）
    /// </summary>
    public void SyncDbFromEntities()
    {
        if (!_env.IsDevelopment())
        {
            _logger.LogWarning("只能在开发环境生成表结构！");
            return;
        }
        // 获取当前程序集中的所有类型
        var assembly = Assembly.Load("3`NovaAdmin.Domain");
        var types = assembly.GetTypes();

        // 扫描标记了 [SugarTable] 特性的类
        var markedTypes = types.Where(type => type.GetCustomAttributes(typeof(SugarTable), true).Any());

        // 遍历这些类型并创建或更新表
        foreach (var type in markedTypes)
        {
            // 自动创建表（如果表不存在）
            _db.CodeFirst.InitTables(type);
        }
    }

    /// <summary>
    /// 控制台调用
    /// </summary>
    /// <param name="args"></param>
    /// <param name="app"></param>
    public static void CreateEntities(string[] args, WebApplication app)
    {
        if (args.Contains("--generate-entities"))
        {
            if (!args.Contains("--generate-entities")) return;

            using var scope = app.Services.CreateScope();
            var migrationHandler = scope.ServiceProvider.GetRequiredService<MigrationHandler>();

            // 解析参数
            var (tables, outputFolder) = ParseCommandLineArgs(args);

            // 执行生成
            migrationHandler.GenerateDomainEntitiesFromDb(outputFolder, tables);

            Environment.Exit(0);//直接退出程序
        }
    }

    /// <summary>
    /// 解析参数
    /// </summary>
    /// <param name="args"></param>
    /// <returns></returns>
    private static (List<string> tables, string outputFolder) ParseCommandLineArgs(string[] args)
    {
        Console.WriteLine("输入参数:");
        foreach (var arg in args)
        {
            Console.WriteLine(arg);
        }

        var tables = new List<string>();
        string outputFolder = string.Empty;

        for (int i = 0; i < args.Length; i++)
        {
            if (args[i] == "--table" && i + 1 < args.Length)
            {
                // 解析表名参数（支持多种分隔符）
                tables.AddRange(
                    args[i + 1].Split(new[] { ',', '，', ' ', ';', '；' },
                    StringSplitOptions.RemoveEmptyEntries)
                    .Select(n => n.Trim())
                    .Where(n => !string.IsNullOrWhiteSpace(n))
                );
            }
            else if (args[i] == "--output" && i + 1 < args.Length)
            {
                // 解析输出目录参数
                outputFolder = args[i + 1].Trim();
            }
        }

        return (tables, outputFolder);
    }
}